﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using RLab.Programming;
using System.Drawing;

namespace rlab.presentation
{
    class EditorControl : RichTextBox
    {
        private bool _paint = true;
        private bool _pasted = false;

        public EditorControl()
        {
            EditorKeyWordColor = Color.Blue;
            EditorBooleanExpressionColor = Color.Gray;
            EditorBuiltInFunctionColor = Color.Fuchsia;
            EditorDefaultColor = Color.Black;
            EditorCommentColor = Color.Green;
            EditorStringColor = Color.Red;
            Font = new System.Drawing.Font( "Consolas", 11.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ( ( byte )( 238 ) ) );
            WordWrap = false;
        }

        public Color EditorKeyWordColor { get; set; }
        public Color EditorBooleanExpressionColor { get; set; }
        public Color EditorBuiltInFunctionColor { get; set; }
        public Color EditorStringColor { get; set; }
        public Color EditorCommentColor { get; set; }
        public Color EditorDefaultColor { get; set; }

        public bool ColorAll { get; set; }

        protected override void OnCreateControl()
        {
            SelectionTabs = Enumerable.Range( 1, 31 ).Select( i => i * 12 ).ToArray();            
            base.OnCreateControl();
        }
 
        protected override void WndProc( ref System.Windows.Forms.Message m )
        {
            if ( m.Msg == 0x00f && !_paint )
            {
                m.Result = IntPtr.Zero;
            }
            else
            {
                base.WndProc( ref m );
            }
        }

        protected override void OnTextChanged( EventArgs e )
        {
            ColorSyntax();
        }

        protected override bool ProcessCmdKey( ref Message m, Keys keyData )
        {
            _pasted = ( keyData & Keys.V ) == Keys.V && ( keyData & Keys.Control ) == Keys.Control;
            return base.ProcessCmdKey( ref m, keyData );
        }  
              
       
        private void ColorSyntax()
        {
            _paint = false;
            try
            {
                var oldsel = new Tuple<int, int>( SelectionStart, SelectionLength );
                int txtOffset;
                var txt = GetLexingText( out txtOffset );
                if ( !this.DesignMode )
                {
                    ColorText( txt, txtOffset );
                }
                Select( oldsel.Item1, oldsel.Item2 );
                SelectionColor = EditorDefaultColor;
            }
            finally
            {
                _paint = true;
            }           
        }

        private void ColorText(  string txt,int txtOffset )
        {
            foreach ( var t in CodeAnalizer.tokenPositions( txt ) )
            {
                Select( t.Item1.Item1 + txtOffset, t.Item1.Item2 );
                switch ( t.Item2 )
                {
                    case 1: SelectionColor = EditorKeyWordColor;
                        break;
                    case 2: SelectionColor = EditorBooleanExpressionColor;
                        break;
                    case 3: SelectionColor = EditorBuiltInFunctionColor;
                        break;
                    case 4: SelectionColor = EditorStringColor;
                        break;
                    case 5: SelectionColor = EditorCommentColor;
                        break;
                    default:
                        SelectionColor = EditorDefaultColor;
                        break;
                }
            }
        }        

        private string GetLexingText(  out int textOffset )
        {
            string result = "";
            if ( ColorAll )
            {
                textOffset = 0;
                result = Text;
            }
            else
            {
                int stopline, startLine;
                stopline = startLine = GetLineFromCharIndex( SelectionStart );
                if ( _pasted )
                {
                    if ( WordWrap )
                    {
                        throw new Exception( "This won't work" );
                    }
                    var clipBoardText = Clipboard.GetText();
                    startLine = GetLineFromCharIndex( Max( SelectionStart - clipBoardText.Length, 0 ) );

                    int firstLine = Max( startLine - 1, 0 );
                    int lastline = Min( stopline + 1, Lines.Length - 1 );
                    int firstchar = GetFirstCharIndexFromLine( firstLine );
                    int lastchar = GetFirstCharIndexFromLine( lastline ) + Lines[lastline].Length;
                    result = Text.Substring( firstchar, lastchar - firstchar );
                    textOffset = GetFirstCharIndexFromLine( firstLine );
                }
                else
                {
                    result = Lines.Length != 0 ? Lines[stopline] : Text;
                    textOffset = GetFirstCharIndexFromLine( startLine );
                }
            }
            
            return result;
        }

        private int Min( int x, int y )
        {
            return x > y ? y : x;
        }

        private int Max( int x, int y )
        {
            return x > y ? x : y;
        }
    }
}
